import _ from 'lodash'
import { nanoid } from 'nanoid'

export function timeFix () {
  const time = new Date()
  const hour = time.getHours()
  return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好'
}

/**
 * 触发 window.resize
 */
export function triggerWindowResizeEvent () {
  const event = document.createEvent('HTMLEvents')
  event.initEvent('resize', true, true)
  event.eventType = 'message'
  window.dispatchEvent(event)
}

export function handleScrollHeader (callback) {
  let timer = 0

  let beforeScrollTop = window.pageYOffset
  callback = callback || function () {
  }
  window.addEventListener(
    'scroll',
    event => {
      clearTimeout(timer)
      timer = setTimeout(() => {
        let direction = 'up'
        const afterScrollTop = window.pageYOffset
        const delta = afterScrollTop - beforeScrollTop
        if (delta === 0) {
          return false
        }
        direction = delta > 0 ? 'down' : 'up'
        callback(direction)
        beforeScrollTop = afterScrollTop
      }, 50)
    },
    false
  )
}

export function isIE () {
  const bw = window.navigator.userAgent
  const compare = (s) => bw.indexOf(s) >= 0
  const ie11 = (() => 'ActiveXObject' in window)()
  return compare('MSIE') || ie11
}

/**
 * Remove loading animate
 * @param id parent element id or class
 * @param timeout
 */
export function removeLoadingAnimate (id = '', timeout = 1500) {
  if (id === '') {
    return
  }
  setTimeout(() => {
    document.body.removeChild(document.getElementById(id))
  }, timeout)
}

// ----------------------------------------------------------------------------------------------------------------------------------------
/**
 * 自定义Utils
 *
 */
/**
 * 生成uuid
 * @param len 长度
 * @param radix  基数
 * @returns 一串随机字符串
 */
export function _getUuid (len, radix) {
  var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
  var uuid = []
  var i
  radix = radix || chars.length

  if (len) {
    // Compact form
    for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]
  } else {
    // rfc4122, version 4 form
    var r

    // rfc4122 requires these characters
    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
    uuid[14] = '4'

    // Fill in random data.  At i==19 set the high bits of clock sequence as
    // per rfc4122, sec. 4.1.5
    for (i = 0; i < 36; i++) {
      if (!uuid[i]) {
        r = 0 | (Math.random() * 16)
        uuid[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r]
      }
    }
  }
  return uuid.join('')
}

// 函数节流
export function _throttle (fn, gapTime) {
  if (gapTime == null || gapTime === undefined) {
    gapTime = 1000
  }
  let _lastTime = null
  // 返回新的函数
  return function () {
    const _nowTime = +new Date()
    if (_nowTime - _lastTime > gapTime || !_lastTime) {
      fn.apply(this, arguments) // 将this和参数传给原函数
      _lastTime = _nowTime
    }
  }
}

// 判断当前环境是什么浏览器
export function _whichBrowser () {
  var userAgent = navigator.userAgent // 取得浏览器的userAgent字符串
  var isOpera = userAgent.indexOf('Opera') > -1
  if (isOpera) {
    return 'Opera'
  }
  if (userAgent.indexOf('Firefox') > -1) {
    return 'FF'
  }
  if (userAgent.indexOf('Chrome') > -1) {
    return 'Chrome'
  }
  if (userAgent.indexOf('Safari') > -1) {
    return 'Safari'
  }
  if (userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 && !isOpera) {
    return 'IE'
  }
  if (userAgent.indexOf('Trident') > -1) {
    return 'Edge'
  }
}

/**
 * 下载图片
 * @param {*} base64 base64格式的图片
 * @param {*} imgName 下载的图片的名称
 */
export function _downLoadImage (base64, imgName) {
  const browser = _whichBrowser()
  if (window.navigator.msSaveOrOpenBlob) {
    var bstr = atob(base64.split(',')[1])
    var n = bstr.length
    var u8arr = new Uint8Array(n)
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    var blob = new Blob([u8arr])
    window.navigator.msSaveOrOpenBlob(blob, imgName + '.png')
  } else {
    var a = document.createElement('a')
    if (typeof a.download === 'undefined') {
      window.location = base64
    } else {
      a.href = base64
      a.download = browser === 'FF' ? imgName + '.png' : imgName
      document.body.appendChild(a)
      a.click()
      a.remove()
    }
  }
}

/**
 * 通过url地址下载文件
 * @param {*} url 文件地址
 * @param {*} fileName 文件名称
 */
export function _downFile (url, fileName) {
  var a = document.createElement('a')
  if (typeof a.download === 'undefined') {
    // 设置 target 属性以在新窗口或新标签页中打开
    window.open(url, '_blank')
  } else {
    a.href = url
    if (fileName) {
      a.download = fileName
    }
    // 设置 target 属性以在新窗口或新标签页中打开
    a.setAttribute('target', '_blank')
    document.body.appendChild(a)
    a.click()
    a.remove()
  }
}

/**
 * 静态路径图片转为base64
 * @param {*} imgUrl 图片路径
 */
export function _img2Base64 (imgUrl) {
  return new Promise((resolve, reject) => {
    const image = new Image()
    // 解决跨域 canvas 污染问题
    image.setAttribute('crossOrigin', '')
    image.src = imgUrl
    image.onload = function () {
      const canvas = document.createElement('canvas')
      canvas.width = image.width
      canvas.height = image.height
      const context = canvas.getContext('2d')
      context.drawImage(image, 0, 0, image.width, image.height)
      resolve(canvas.toDataURL())
    }
  })
}

/**
 * blob类型的图片转为base64
 * @param {*} blob  图片流
 */
export function _blob2Base64 (blob) {
  return new Promise((resolve, reject) => {
    const oFileReader = new FileReader()
    oFileReader.onloadend = function (e) {
      const base64 = e.target.result
      resolve(base64)
    }
    oFileReader.readAsDataURL(blob)
  })
}
/**
 * 将 Blob 对象转换为 Base64 编码的字符串。
 *
 * @param {Blob} blob - 需要转换的 Blob 对象。
 * @returns {Promise<string>} - 返回一个 Promise 对象，成功时返回 Base64 编码的字符串（图片流），失败时返回错误信息。
 */
export function convertBlobToBase64 (blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(blob)
    reader.onloadend = () => resolve(reader.result.split(',')[1])
    reader.onerror = error => reject(error)
  })
}

/**
 * 将File类型的图片转为base64格式
 * @param {*} file  要转化为base64的File对象
 * @param {*} callback  成功后的回调
 */
export function _file2Base64 (file, callback) {
  const reader = new FileReader()
  reader.addEventListener('load', () => callback(reader.result))
  reader.readAsDataURL(file)
}

/**
 * 将以base64的图片url数据转换为Blob
 * @param {*} file  用url方式表示的base64图片数据
 * @param {*} callback  文件名
 */
export function _dataURLtoFile (base64, filename) { // 将base64转换为文件
  var arr = base64.split(',')
  var mime = arr[0].match(/:(.*?);/)[1]
  var bstr = atob(arr[1])
  var n = bstr.length
  var u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], filename, { type: mime })
}

/**
 * arrayBuffer 转换为字符串
 * @param {*} buffer  arrayBuffer对象
 */
export function _arrayBuffer2String (buffer) {
  return new Promise((resolve, reject) => {
    const oBuffer = new Blob([buffer])
    var fileReader = new FileReader()
    fileReader.readAsText(oBuffer, 'utf-8')
    fileReader.onload = function () {
      if (fileReader) {
        const result = JSON.parse(fileReader.result)
        resolve(result)
      }
    }
  })
}

/**
 * 数量逢3断1
 * @example 10000
 * @returns 10,000
 * @param value
 */
export function _NumberFormat (value) {
  if (value === null || value === undefined) { // 检查 null 或 undefined
    return '0'
  }

  if (typeof value === 'number') { // 检查是否为数字类型
    return value.toLocaleString()
  }

  if (typeof value === 'string') { // 检查是否为字符串类型
    const num = Number(value) // 尝试转换为数字
    if (!isNaN(num)) { // 检查转换是否成功
      return num.toLocaleString()
    }
  }

  return '0' // 其他类型，返回默认值 '0'
}

/**
 * @method clearNullValues 处理对象空属性,清除空值属性
 * @return { Object} 返回新对象
 */
export const clearNullValues = (data) => {
  if (Array.isArray(data)) {
    return data.filter(item => ![null, '', NaN, undefined].includes(item))
  }
  const _data = _.cloneDeep(data)
  for (const key in _data) {
    if (['', null, NaN, undefined].includes(_data[key])) {
      delete _data[key]
    }
  }
  return _data
}

/*
*
 * @param {Array<Object>} treeData - 待搜索的树形数据数组，每个元素代表一个节点对象
 * @param {string} searchField - 要匹配的字段名称
 * @param {*} searchValue - 匹配字段应具有的值
 * @returns {Object|undefined} - 找到的第一个匹配节点对象，未找到则返回undefined
 *
* */
export function findObjectByField (treeData, searchField, searchValue) {
  // 遍历树数据
  for (const node of treeData) {
    // 检查当前节点是否包含搜索的字段并且该字段的值与searchValue匹配
    if (_.has(node, searchField) && node[searchField] === searchValue) {
      // 如果匹配，返回整个节点对象
      return node
    }
    // 如果当前节点有子节点，继续在子节点中查找
    if (node.children && node.children.length > 0) {
      const foundInChildren = findObjectByField(node.children, searchField, searchValue)
      // 如果在子节点中找到了匹配项，直接返回该对象
      if (foundInChildren !== undefined) {
        return foundInChildren
      }
    }
  }
  // 如果遍历完整棵树都没有找到匹配项，返回undefined
  return undefined
}

/**
 * 过滤树结构中指定字段值的节点，支持过滤多个值。
 *
 * @param {Array} treeData - 待过滤的树结构数据，数组形式。
 * @param {String} fieldName - 需要检查的字段名。
 * @param {Array} filterValues - 需要过滤掉的字段值组成的数组。
 * @returns {Array} 过滤后的树结构数据，不包含任何指定字段值的节点。
 */
export function filterNodesByFieldValue (treeData, fieldName, filterValues) {
  // 确保filterValues是一个数组，即使传递的是单个值
  filterValues = Array.isArray(filterValues) ? filterValues : [filterValues]

  // 使用Array的filter方法遍历树结构的当前层级，
  // 过滤掉那些在指定字段fieldName上的值出现在filterValues数组中的节点。
  return treeData
    .filter(node => !filterValues.includes(node[fieldName]))

    // 使用map方法递归处理过滤后的每个节点的子节点。
    .map(node => {
      if (node.children && node.children.length > 0) {
        // 递归调用filterNodesByFieldValue，更新当前节点的children为过滤后的子节点数组。
        node.children = filterNodesByFieldValue(node.children, fieldName, filterValues)
      }
      return node
    })
}

// 此函数接收一个数值和一个目标类型（'string', 'int', 'float', 'double'之一）
export function convertType (value, targetType = 'string') {
  try {
    if (!value) return value
    switch (targetType.toLowerCase()) {
      case 'string':
        return String(value)
      case 'int': // 整数转换
        return !isNaN(+value) && value !== 0 ? +value : value
      case 'float': // 浮点数转换
        return !isNaN(+value) && value !== 0 ? +value : value
      case 'double': // double类型
        return !isNaN(+value) && value !== 0 ? +value : value
      default:
        return value
    }
  } catch (e) {
    console.info('转化数据报错')
    return value
  }
}

/**
 * 自定义合并逻辑函数，用于_.mergeWith。
 * 当目标对象的值为空时（通过customIsEmpty判断），使用源对象的值。
 * @param {*} objValue 目标对象中对应键的值。
 * @param {*} srcValue 源对象中对应键的值。
 * @param {Function} customIsEmpty 自定义的空值判断函数。
 * @returns {*} 如果objValue为空且srcValue非空，则返回srcValue，否则返回undefined让lodash执行默认逻辑。
 */
export function customMerger (objValue, srcValue, customIsEmpty = _.isEmpty) {
  if (customIsEmpty(srcValue) && !customIsEmpty(objValue)) {
    return objValue
  }
  return srcValue
}

/**
 * 合并对象，优先使用非空的值。
 * @param {Object} target 目标对象。
 * @param {...Object} sources 源对象列表。
 * @param {Function} [customIsEmpty=_.isEmpty] 自定义的空值判断函数。
 * @returns {Object} 合并后的对象。
 */
export function mergeWithNonEmpty (target, ...sources) {
  const customIsEmpty = function (value) {
    return value === undefined || value === null
  } // 如果最后一个参数是自定义的isEmpty函数，则取出它
  return _.mergeWith({}, target, ...sources, (objValue, srcValue) => customMerger(objValue, srcValue, customIsEmpty))
}

export function setTableRulesData (data) {
  const { infoCheckAddDTOList, infoCheckQueryDTOList, viewCheckQueryDTOList, viewCheckAddDTOList } = data
  const DTOList = infoCheckQueryDTOList || infoCheckAddDTOList || viewCheckQueryDTOList || viewCheckAddDTOList
  return DTOList?.filter(item => item.changeRule !== 'notNull').map(item => {
    return {
      ...item,
      fields: item.columnKey.split(','),
      cid: nanoid(10),
      ruleInfo: item.ruleInfo,
      min: item.ruleInfo.indexOf('~') > -1 ? item.ruleInfo.split('~')[0] : '',
      max: item.ruleInfo.indexOf('~') > -1 ? item.ruleInfo.split('~')[1] : ''
    }
  }) || []
}

export function repairUiruler (formUi, data) {
  // 添加校验
  formUi.rulesData = setTableRulesData(data)
  return formUi
}

export function repairUiSort (formUi, sort) {
  // 替换sort
  formUi.basicData.sort = sort
  return formUi
}
// buttonData没替换
export function repairUiButtonData (formUi, data) {
  // 替换ButtonData
  if (formUi.buttonData && data.tableButtonReqDTOList) {
    formUi.buttonData.forEach(uiItem => {
      data.tableButtonReqDTOList.forEach(item => {
        if (item.buttonEventAddDTO.eventType === 'report' && item.dataSourceExpandAddDTOList?.length > 0 && item.tableButtonAddDTO.buttonName === uiItem.tableButtonAddDTO.buttonName) {
          uiItem.buttonEventAddDTO.foreignId = item.buttonEventAddDTO.foreignId
        }
        if (!uiItem.displayFields && uiItem.displayFieIds) {
          uiItem.displayFields = uiItem.displayFieIds
          delete uiItem.displayFieIds
          delete item.displayFieIds
        }
      })
    delete uiItem.displayFieIds
    })
  }
  return formUi
}

/**
 * 修复 UI 数据，根据 dataIdMap 更新 formUi 中的数据源 ID
 * @param {string} jsonUi - 要解析的 UI 数据 JSON 字符串
 * @param {Array} dataIdMap - 包含数据源映射信息的数组
 * @returns {Object} - 更新后的 UI 数据对象
 */
export function repairUiData (jsonUi, dataIdMap) {
  // 将 JSON 字符串解析为 JavaScript 对象
  const formUi = JSON.parse(jsonUi)
  // TODO 后期可删除 这是为了解决ui出错问题
  // eslint-disable-next-line no-unused-expressions
  formUi?.pageData?.forEach(item => {
    if (item.formData.dataSource === 'dateSource') {
      item.formData.dataSource = 'dataSource'
    }
    if (item.formData.type === 'table' && item.formData.dataSource === 'alone') {
      item.isSendData = true
    }
    if (item.formData.hasOwnProperty('dataShowFields')) {
      delete item.formData.dataShowFieIds
    } else if (item.formData.dataShowFieIds) {
      item.formData.dataShowFields = item.formData.dataShowFieIds
    }
  })
  // 如果 dataIdMap 存在且非空，则遍历每个数据源映射项
  if (dataIdMap && dataIdMap.length > 0) {
    dataIdMap.forEach(item => {
      // 查找与当前 item.dataColumnKey 匹配的 pageData 元素
      const matchedElement = formUi.pageData.find(e =>
        item.dataColumnKey === e.formData.dataIndex
      )

      // 如果找到了匹配项，则更新其 dataSourceId
      try {
        if (matchedElement && item.type === 'change') {
          const dataConfig = matchedElement.formData.linkageConfig.dataConfig
          if (!_.isArray(dataConfig) && !_.isEmpty(dataConfig)) {
            dataConfig.plugInfo.dataId = item.dataId
            matchedElement.formData.linkageConfig.dataConfig = [dataConfig]
          } else if (dataConfig) {
            dataConfig.some(e => {
               if (e.cid === item.ruleId) { // 匹配上则更新dataId
                 e.plugInfo.dataId = item.dataId
               }
            })
          }
        } else if (matchedElement && item.type === 'query') {
          matchedElement.formData.dataSourceId = item.dataId
        } else if (matchedElement && item.type === 'check') {
          matchedElement.formData.validateCondition.dataSourceId = item.dataId
        } else if (matchedElement && item.type === 'tableColumn') {
          const { cid } = matchedElement.formData.columns?.find(e => {
            if (e.cid === item.ruleId) {
              e.dataId = item.dataId
              return true
            }
          }) || {}
          if (cid) {
            matchedElement.formData.columns.forEach(e => {
              if (cid === e.pCid && e.controlType === 'sourceKey') { // 展示字段匹配到pCid,dataId也要修改
                e.dataId = item.dataId
              }
            })
          }
        }
      } catch (e) {
        console.error('数据源映射失败', e)
      }
    })
  }

  // 返回更新后的 formUi 对象
  return formUi
}

/**
 * 将文本进行 Base64 编码
 *
 * 此函数使用内置的 btoa 函数来实现 Base64 编码它首先使用 encodeURIComponent 对文本进行编码，
 * 然后使用 unescape 将编码后的文本中的 '%' 转换为 '+'，最后使用 btoa 函数进行 Base64 编码
 *
 * @param {string} text - 需要被编码的文本
 * @return {string} - 编码后的 Base64 字符串
 */
export function base64Encode (text) {
  // 使用 btoa 函数进行 base64 编码
  return btoa(unescape(encodeURIComponent(text)))
}

/**
 * 将对象中的所有 `undefined` 值替换为空字符串。
 *
 * @param {Object} obj - 需要处理的对象。
 * @returns {Object} - 处理后的对象，其中所有的 `undefined` 值都被替换为空字符串。
 */
// 深度转换函数
export function convertUndefinedToEmptyString (obj) {
  if (_.isArray(obj)) {
    // 如果是数组，递归处理每个元素
    return obj.map(item => convertUndefinedToEmptyString(item))
  } else if (_.isObject(obj)) {
    // 如果是对象，递归处理每个属性
    return _.mapValues(obj, value =>
      convertUndefinedToEmptyString(value)
    )
  } else {
    // 如果是基本类型，检查是否为 undefined 并替换
    return obj === undefined ? null : obj
  }
}

/**
 * @method money2Fixed
 * @description 处理金额精度的过滤函数
 * @param {Number} money 需要处理的金额
 * @param {Number} s 保留小数后位数 (用于四舍五入)
 * @param {Number} h 显示保留小数点后位数 (决定输出格式)
 * @return {Number|String} 参数h大于0，返回格式为字符串,否则为数字类型
 */
export function money2Fixed (money, s = 2, h = 0) {
  if (money === undefined || money === null || money === '') {
    return 0
  }
  // 检查是否为有效数字
  if (typeof money !== 'number' || isNaN(money)) {
    return h ? 'Invalid number' : NaN
  }

  // 确保传入的小数位数是非负整数
  s = Math.max(0, ~~s)
  h = Math.max(0, ~~h)

  // 使用 Math.round 四舍五入到指定的小数位数
  const factor = Math.pow(10, s)
  const roundedMoney = Math.round(money * factor) / factor

  // 根据参数 h 决定返回值的格式
  if (h > 0) {
    return roundedMoney.toFixed(h)
  } else {
    // 如果不要求特定显示的小数位数，则不使用 toFixed() 以保持数值类型
    return Number(roundedMoney.toFixed(s))
  }
}
